Take Off Environment OEI AFM Procedure¶

Introduction¶

This notebook deals with a realistic model of the aircraft at take-off conditions. We deal with transport-type airplanes and write the general take-off equations. There are several different types of aircraft take-off. We will refer exclusively to Conventional Take-Off.

Considerations¶

We consider a take-off operation on a vertical plane. Due to the level of approximations that it is possible to achieve, in the calculation of the take-off of a conventional airplane, we will assume that the runway reference system and the ground reference system are equivalent.

The sequence of airplane speeds during take-off are given in the figure below.

img/takeoff_phases.png

The airplane accelerates, the nose landing gear lifts off and the airplane rotates with a speed $V_R$; the airplane lifts off the ground with a speed $V_{LOF}$ and follows a rectilinear flight path to clear a conventional screen at 35 feet from the ground.

The take-off is divided into three segments:

  • ground run with all wheels on the runway to the point of nose wheels liftoff (Break release up to $V_{R}$)
  • ground run to the point of lift-off (all wheels off the ground) ($V_{R}$ up to $V_{LOF}$)
  • airborne phase to the height of the screen ($V_{LOF}$ up to $V_{35ft}$)

Ground Run¶

The ground run is the distance between brake-release and lift-off of the forward wheels. To calculate the ground run, we write the dynamics equations on the centre of gravity of the aircraft in the horizontal and vertical directions, for a take-off from a horizontal runway. The engine thrust is aligned with the vector velocity.

Governing equations:

  • $m * du/dt = T - D - R$
  • $R = \mu * (L-W)$
  • $D = 0.5 * \rho * S * C_d * TAS^2$
  • $L = 0.5 * \rho * S * C_l * TAS^2$

State Increments:

  • $dv =accel * dt$
  • $dx = 0.5 * accel * dt^2 + TAS * dt$

The thrust is given by the thrust model which takes as an input the speed.

Target: Reach $V_R$

Rotation and Initial Climb¶

The lift-off point is reached with at least one landing gear on the ground. The rotation of the aircraft must be done with a small angle or else there is a risk of a tail strike on the runway. The rotation run is the distance between lift-off of the forward wheels and lift-off of the main landing gears.

Governing equations:

  • $m * du/dt = T - D(\alpha) - R$
  • $R = \mu * (L(\alpha)-W)$
  • $D = 0.5 * \rho * S * C_d(\alpha) * TAS^2$
  • $L = 0.5 * \rho * S * C_l(\alpha) * TAS^2$

Constant: $q = Rotation Rate$

State Increments:

  • $dv =accel * dt$
  • $dx = 0.5 * accel * dt^2 + TAS * dt$
  • $\alpha = q * dt + \alpha_0$

Target : $W = L(\alpha)$

Airborne phase to the height of the screen ($V_{LOF}$ up to $V_{35ft}$)¶

img/climb.png

In this segment we will try to compute the state of the aircraft from $V_{LOF}$ up to point A.

Governing equations:

  • $m * dU/dt = T\cos(\alpha + \epsilon) - D - W\sin\gamma$
  • $mU * d\gamma/dt = T\sin(\alpha + \epsilon) + L - W\cos\gamma$
  • $dx/dt = V_g\cos\gamma$
  • $dh/dt = V_g\sin\gamma$

Target: Reach 35ft

The climb velocity in general accelerated flight is

$v_c = \frac{T-D}{W}U - \frac{U}{g}dU/dt$

In our case we want to climb with a max rate of climb, thus our target is to minimize the accelaration. The pilot can control the acceleration of the a/c with the stick input, thus the change of $\alpha$. The easiest way to model this procedure is to create a simple gain control loop.

  • Control Variable: acceleration
  • Target: 0 acceleration
  • Manipulated variable: $\alpha$

A simple schema of the control function is illustrated below:

img/control_loop.png

TakeOff Procedure¶

Import Libs¶

In [1]:
# Post Processing
import pandas as pd
import numpy as np

# Plotting
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots

# Take Off Segments
from src.modules.take_off_env import TakeOffPrep
from src.modules.ground_phase import GroundRoll
from src.modules.rotation_phase import RotationPhase
from src.modules.airborne_phase import AirbornePhase

Inputs¶

In [2]:
input_variables = {
    "mass": 80e3,  # [kg]
    "conf": "1+F", 
    "zp": 0,       # [ft]
    "lg": "Up",
    "engine_state": "OEI",
    "timestep": 1e-1,
}

Methodology Part 1¶

Before initiating the TakeOff Procedure some preliminary steps have to be made first.

  • Define $V_{stall}$ which is the speed that we will base all our calculations
  • Define all other characteristic speeds $(V_r, V_{EF}, V_{MCA}, V_{2min})$
  • Check that we are compliant with JAR25.121b

A dedicated class was created to prepare the take-off

In [3]:
example_preperation = TakeOffPrep(input_variables)

Calculate Stall Speed¶

We calculate the stall speed with the following equation: $ V_{stall} = \sqrt(\frac{2 * mg}{(\rho S C_{Lmax}}) $

In [4]:
example_preperation.calculate_stall_speed()
print(f"Stall Speed  = {round(example_preperation.v_sta, 2)}kt")
Stall Speed  = 129.71kt

Define the rest of the characteristic speeds¶

In [5]:
example_preperation.define_characteristic_speeds()
print("Characteristic Speeds\n")
for key, value in example_preperation.speeds.items():
    print (f"{key} = {round(float(value), 2)}kt")
Characteristic Speeds

v2min = 152.22kt
vr = 136.2kt
vef = 134.2kt
vmca = 114.6kt
v_stall = 129.71kt

Check that we are compliant with the JAR25.121b Regulation¶

In the case of non compliance of $V_{2min}$ with JAR25.121b, we take the ssg of JAR(2.4%) and we calculate the new $V_2$

In [6]:
example_preperation.calculate_v2_jar()
JAR Assertion: SSG of V2min 4.7% Passed

All these methods are summed up into one function in the same class called pilot_preparation. pilot_prep-3.png

Methodology Part 2¶

Now that we have our characteristic speeds, we can start our take off procedure. The idea here is that we start with the minimum $V_R$ and we try to reach $V_2$ until we reach 35ft.

If at any point of the flight the corresponding speed does not pass the criteria then we incrementally increase the $V_R$ by $1\% V_{stall}$ and we start the iterative process again until all the criteria are met.

In [7]:
class TakeOffProc(GroundRoll, RotationPhase, AirbornePhase):

    def check_vr(self) -> None:

        if self.variables["need_to_increase_Vr"]:
            self.speeds["vr"] += 0.01 * self.speeds["v_stall"]
            self.speeds["vef"] = self.speeds["vr"] - 2
            print(f"VR increased at {self.speeds['vr']} kt")
            super().initialize_data()

    def takeoff(self) -> None:

        super().pilot_preparation()  # calculate all the characteristic speeds
        print(f"Vr = {self.speeds['vr']} kt")
        while self.variables["cas_kt"] < self.speeds["v_target"]:
            super().up_to_rotation()  # 1st segment of takeoff
            super().transition_phase()  # 2nd segment of takeoff
            self.check_vr()
            if not self.variables["need_to_increase_Vr"]:
                super().airborne_phase()  # 3rd segment of takeoff
                self.check_vr()

The above class inherits the 3 takeoff segments, which correspondingly inherit the take-off preparation class. In case that the $V_R$ does not pass the take-off critera, we then increase it incrementally by 1% of the stall speed until all the criteria pass.

Create the object and run the simulation¶

In [8]:
amazing_aircraft = TakeOffProc(input_variables)
amazing_aircraft.takeoff()
JAR Assertion: SSG of V2min 4.7% Passed

Vr = 136.20010905033593 kt
Could not reach V2
Increase VR the a/c cannot reach the target :(

VR increased at 137.4972529460534 kt
Could not reach V2
Increase VR the a/c cannot reach the target :(

VR increased at 138.7943968417709 kt
Could not reach V2
Increase VR the a/c cannot reach the target :(

VR increased at 140.09154073748837 kt
Could not reach V2
Increase VR the a/c cannot reach the target :(

VR increased at 141.38868463320586 kt
Could not reach V2
Increase VR the a/c cannot reach the target :(

VR increased at 142.68582852892334 kt
Could not reach V2
Increase VR the a/c cannot reach the target :(

VR increased at 143.98297242464082 kt
Could not reach V2
Increase VR the a/c cannot reach the target :(

VR increased at 145.2801163203583 kt
V2min reached @ 0.18ft!

PostProcessing¶

So, the algorithm finally converged to a specific $V_R$!

In order to post process the results we are going to transform the log dictionary to a pandas dataframe.

In [9]:
df = pd.DataFrame(amazing_aircraft.event_log)
df["accel_cas"] = np.gradient(df["cas_kt_log"], df["t_log"], edge_order=2)
df["accel_tas"] = np.gradient(df["tas_kt_log"], df["t_log"], edge_order=2)
display(df.tail(50)) 
t_log x_log height_log cas_kt_log tas_kt_log vz_log teta_log alpha_log gamma_log thrust_log lift_log drag_log accel_cas accel_tas
340 34.0 1423.667079 0.000000 151.845921 151.845921 0.000000 12.200000 12.200000 0.000000 104606.470312 762779.300608 64742.875097 0.882056 0.882056
341 34.1 1431.480928 0.000000 151.931529 151.931529 0.000000 12.500000 12.500000 0.000000 104606.470312 777337.380966 66851.161006 0.824255 0.824255
342 34.2 1439.299017 0.000000 152.010772 152.010772 0.000000 12.800000 12.800000 0.000000 104606.470312 791874.500481 69349.890072 0.746277 0.746402
343 34.3 1447.119144 0.011222 152.080785 152.080810 0.039054 13.236276 13.207662 0.028614 104606.470312 809946.060153 72633.586554 0.653400 0.653850
344 34.4 1454.942868 0.040393 152.141452 152.141542 0.101515 13.642181 13.567838 0.074343 104606.470312 827101.898675 75701.093454 0.557485 0.558405
345 34.5 1462.769702 0.093881 152.192282 152.192491 0.186136 14.016199 13.879940 0.136259 104606.470312 841263.927558 78767.126302 0.463172 0.464692
346 34.6 1470.599124 0.176944 152.234087 152.234481 0.289062 14.352964 14.141429 0.211535 104606.470312 852960.179685 81334.840063 0.376894 0.379122
347 34.7 1478.430655 0.293928 152.267660 152.268315 0.407104 14.654330 14.356494 0.297836 104606.470312 862461.256660 83444.139965 0.298145 0.301169
348 34.8 1486.263848 0.448360 152.293716 152.294715 0.537421 14.922302 14.529212 0.393090 104606.470312 870098.880250 85155.521479 0.227196 0.231087
349 34.9 1494.098290 0.643070 152.313100 152.314533 0.677591 15.158784 14.663252 0.495532 104606.470312 876030.609986 86436.851415 0.164880 0.169693
350 35.0 1501.933610 0.880254 152.326692 152.328653 0.825402 15.366525 14.762972 0.603553 104606.470312 880446.062422 87295.227152 0.109521 0.115300
351 35.1 1509.769479 1.161545 152.335004 152.337593 0.978890 15.548622 14.832896 0.715726 104606.470312 883543.491806 87899.762019 0.059232 0.066007
352 35.2 1517.605596 1.488080 152.338538 152.341855 1.136342 15.706465 14.875657 0.830808 104606.470312 885438.232609 88270.533903 0.013882 0.021673
353 35.3 1525.441684 1.860551 152.337780 152.341927 1.296199 15.841505 14.893838 0.947667 104606.470312 886243.956573 88428.424686 -0.026705 -0.017888
354 35.4 1533.277494 2.279243 152.333197 152.338277 1.457049 15.955219 14.889941 1.065278 104606.470312 886071.211913 88394.562076 -0.062746 -0.052900
355 35.5 1541.112800 2.744078 152.325231 152.331347 1.617625 16.049084 14.866362 1.182722 104606.470312 885026.325063 88189.866952 -0.094478 -0.083611
356 35.6 1548.947405 3.254652 152.314301 152.321555 1.776798 16.124562 14.825382 1.299180 104606.470312 883210.613763 87834.698297 -0.122158 -0.110281
357 35.7 1556.781135 3.810277 152.300800 152.309291 1.933574 16.183080 14.769154 1.413926 104606.470312 880719.860945 87348.585219 -0.146045 -0.133177
358 35.8 1564.613840 4.410013 152.285092 152.294920 2.087081 16.226022 14.699695 1.526327 104606.470312 877644.002003 86750.034363 -0.166327 -0.152492
359 35.9 1572.445393 5.052705 152.267534 152.278793 2.236567 16.254722 14.618890 1.635832 104606.470312 874066.982556 86050.207911 -0.181777 -0.167005
360 36.0 1580.275689 5.737012 152.248737 152.261519 2.381388 16.270534 14.528563 1.741971 104606.470312 870070.173685 85149.060724 -0.192796 -0.177118
361 36.1 1588.104658 6.461441 152.228975 152.243369 2.521014 16.276210 14.431861 1.844350 104606.470312 865793.221769 84189.011016 -0.201213 -0.184663
362 36.2 1595.932250 7.224397 152.208494 152.224586 2.655088 16.272902 14.330197 1.942705 104606.470312 861299.001676 83184.940177 -0.207307 -0.189922
363 36.3 1603.758429 8.024207 152.187514 152.205385 2.783337 16.261668 14.224835 2.036834 104606.470312 856643.670970 82150.010335 -0.211332 -0.193151
364 36.4 1611.583176 8.859136 152.166228 152.185956 2.905556 16.243482 14.116901 2.126582 104606.470312 851877.223413 81095.796416 -0.213523 -0.194584
365 36.5 1619.406483 9.727415 152.144809 152.166468 3.021608 16.219241 14.007397 2.211844 104606.470312 847044.016751 80032.419411 -0.213722 -0.194067
366 36.6 1627.228355 10.627246 152.123484 152.147143 3.131412 16.189767 13.897209 2.292558 104606.470312 842048.208140 78938.339050 -0.212129 -0.191797
367 36.7 1635.048811 11.556778 152.102383 152.128108 3.234771 16.156072 13.787500 2.368572 104606.470312 837067.101407 77853.444913 -0.209323 -0.188355
368 36.8 1642.867880 12.514165 152.081619 152.109471 3.331709 16.118851 13.678951 2.439900 104606.470312 832141.334900 76786.526957 -0.205491 -0.183929
369 36.9 1650.685599 13.497585 152.061285 152.091323 3.422302 16.078722 13.572129 2.506592 104606.470312 827296.508227 75742.890480 -0.202096 -0.179979
370 37.0 1658.502008 14.505245 152.041200 152.073476 3.506657 16.036247 13.467523 2.568724 104606.470312 822554.633794 74835.397854 -0.199587 -0.176954
371 37.1 1666.317144 15.535390 152.021368 152.055932 3.584902 15.990583 13.364194 2.626389 104606.470312 817873.086796 73986.072531 -0.196518 -0.173410
372 37.2 1674.131042 16.586284 152.001896 152.038794 3.657113 15.941810 13.262170 2.679641 104606.470312 813252.965892 73152.328193 -0.192446 -0.168899
373 37.3 1681.943744 17.656219 151.982879 152.022152 3.723375 15.890534 13.161998 2.728536 104606.470312 808719.032276 72338.425508 -0.187493 -0.163546
374 37.4 1689.755298 18.743518 151.964397 152.006084 3.783799 15.837318 13.064164 2.773154 104606.470312 804293.030710 71547.998515 -0.181613 -0.157302
375 37.5 1697.565758 19.846544 151.946556 151.990692 3.838529 15.782683 12.969088 2.813595 104606.470312 799919.907899 70770.685183 -0.174600 -0.149961
376 37.6 1705.375182 20.963681 151.929477 151.976092 3.887638 15.727214 12.877304 2.849910 104606.470312 795551.914480 69997.635966 -0.166762 -0.141830
377 37.7 1713.183636 22.093320 151.913204 151.962325 3.931143 15.671550 12.789444 2.882106 104606.470312 791372.503785 69261.680531 -0.158547 -0.133356
378 37.8 1720.991189 23.233920 151.897768 151.949421 3.969288 15.616084 12.705724 2.910360 104606.470312 787391.770148 68564.093525 -0.150041 -0.124626
379 37.9 1728.797907 24.384015 151.883195 151.937400 4.002330 15.561174 12.626317 2.934857 104606.470312 783617.578222 67905.751308 -0.141327 -0.115718
380 38.0 1736.603862 25.542215 151.869503 151.926277 4.030535 15.507139 12.551348 2.955791 104606.470312 780055.739441 67287.175104 -0.132879 -0.107105
381 38.1 1744.409121 26.707208 151.856619 151.915979 4.054176 15.454268 12.480906 2.973362 104606.470312 776710.177273 66741.568295 -0.125288 -0.099377
382 38.2 1752.213749 27.877762 151.844445 151.906402 4.073528 15.402400 12.414630 2.987771 104606.470312 773563.502673 66278.947450 -0.118056 -0.092035
383 38.3 1760.017803 29.052717 151.833008 151.897572 4.088844 15.351203 12.351999 2.999204 104606.470312 770590.879831 65843.530409 -0.110570 -0.084464
384 38.4 1767.821342 30.230977 151.822331 151.889509 4.100347 15.300987 12.293163 3.007824 104606.470312 767799.221613 65436.049565 -0.102902 -0.076734
385 38.5 1775.624424 31.411515 151.812428 151.882225 4.108271 15.252036 12.238234 3.013802 104606.470312 765193.661979 65056.981944 -0.095118 -0.068910
386 38.6 1783.427107 32.593370 151.803307 151.875727 4.112855 15.204601 12.187288 3.017313 104606.470312 762777.665006 64706.571138 -0.087280 -0.061052
387 38.7 1791.229447 33.775652 151.794972 151.870015 4.114342 15.158903 12.140368 3.018534 104606.470312 760553.133590 64384.848052 -0.079446 -0.053216
388 38.8 1799.031500 34.957543 151.787418 151.865084 4.112980 15.115134 12.097487 3.017648 104606.470312 758520.517258 64091.650459 -0.071670 -0.045455
389 38.9 1806.833320 36.138294 151.780638 151.860924 4.109014 15.073460 12.058627 3.014833 104606.470312 756678.919064 63826.641429 -0.063935 -0.037743

Characteristic Instants¶

Now we are going to check the time and speed values that the algorithm found about the characteristic speeds.

In [10]:
pd.DataFrame(amazing_aircraft.characteristic_instants)
Out[10]:
Rotation LiftOff v35ft v2
Instant 30.00 34.20 38.90 34.60
Speed 145.39 152.01 151.78 152.23

We can see that the rotation started 30 seconds after the break release.

Then 4 seconds later the aircraft lifts off!

Almost 9 seconds after the aircraft rotation is already at 35ft!

We can see that $V_{LOF}$ is very close to $V_2$, this makes sense since we want to use most of the a/c energy to climb meaning that we should expect to see a pretty steep curve of $V_z(t)$

Remark: We can see that there is a difference between the values $(V_2, V_{35ft}) $. This is due to the effect of the controller. The acceleration is never 0 but oscillates around this value. Thus with non zero acceleration we can expect to have small differences in the values of speed at the airborne phase.

Initial characteristic speeds¶

In [11]:
pd.DataFrame(amazing_aircraft.speeds)
Out[11]:
v2min vr vef vmca v_stall v_target
0 152.221103 145.280116 143.280116 114.5999 129.71439 152.221103

Remark: The $V_R$ is not exactly the same with the above results due to the sensitivity of the timestep. If we reduce the timestep then the increments dx and dv should tend to be continuous thus, reach this value

Plots¶

$x = f(t)$¶

In [12]:
fig = px.line(df, x='t_log', y= "x_log")
# Edit the layout
fig.update_layout(title='Distance in function of time',
                   xaxis_title='Time[s]',
                   yaxis_title='Distance x_ground-axis[m]')
fig.show()

The above curve has the expected shape, since in the ground segment the a/c follows an non-constant accelerating motion and then for the airborne phase should be more or less linear since we try to have zero acceleration.

In fact we can see that up to 34 seconds the curve follows a concave up shape and then it tends to become linear.

$TAS = f(t)$¶

In [13]:
fig = px.line(df, x='t_log', y= "tas_kt_log")
fig.update_layout(title='True Air Speed in function of time',
                   xaxis_title='Time [s]',
                   yaxis_title='TAS [kt]')
fig.show()

In the above graph we can see that the velocity is augmentating in a non linear manner up to 34.2 seconds, which makes perfect sense since the acceleration is not constant in the ground phase, and then tends to stabilize in the $V_2$ value, which again makes sense since the pilot is targeting zero acceleration in the airborne phase in order to climb as fast as possible.

$V_z = f(t)$¶

In [14]:
fig = px.line(df, x='t_log', y= "vz_log")
fig.update_layout(title='Rate of Climb in function of time',
                   xaxis_title='Time [s]',
                   yaxis_title='Rate of Climb [m/s]')
fig.show()

As expected the rate of climb is 0 until lift-off(34.2 secs) and then we see a rapid increase due to the effort of max climb. The peak of this curve marks the peak of the climb gradient, since $V_z = TAS\sin\gamma$ and TAS is quasi static.

Angles Graph¶

In [15]:
fig = px.line(df, x='t_log', y=["alpha_log", "teta_log", "gamma_log"])
fig.update_layout(title='Aoa, teta & gamma in function of time',
                   xaxis_title='Time[s]',
                   yaxis_title='Degrees')
fig.show()

In this graph we can see that until lift-off $\alpha$ and $\theta$ have the same value since $\gamma$ is 0. When the a/c is airborne and tries to climb the value of gamma goes up. $\gamma$ is approximately given by the following formula:

$\gamma = \frac{T\cos\alpha -D}{W}$

This formula makes evident that in order to increase $\gamma$ we have to lower $\alpha$. This happens due to two main reasons.

  • As we lower $\alpha$ the value of Drag decreases
  • As we lower $\alpha$ the $\cos\alpha$ increases thus we have more thrust

For $\alpha$ we see that it increases until it reaches the target acceleration and then starts to decrease.

$\theta$ is calculated by the known formula :

$\theta = \alpha + \gamma$

$ \alpha, aoa = f(t)$¶

In [16]:
# Create figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])

# Add traces
fig.add_trace(
    go.Scatter(x=df['t_log'], y=df["accel_tas"], name="Acceleration"),
    secondary_y=False,
)

fig.add_trace(
    go.Scatter(x=df['t_log'], y=df["alpha_log"], name="Angle of Attack"),
    secondary_y=True,
)

# Add figure title
fig.update_layout(
    title_text="Acceleration and Angle of Attack in function of time"
)

# Set x-axis title
fig.update_xaxes(title_text="Time[s]")

# Set y-axes titles
fig.update_yaxes(title_text="TASA [m/s2]", secondary_y=False)
fig.update_yaxes(title_text="Alpha [deg]", secondary_y=True)

fig.show()

As far as $\alpha$ is concerned, as stated in the definition of the airborne segment, is the tool that the pilot uses it in order to control the acceleration. When the a/c is airborne with a positive acceleration the pilot pull the stick in order to increase $\alpha$ and decrease the acceleration. We can see that at the peak of $\alpha$ the accleration is very close to zero. Then, the accelarion goes negative and the pilot pushes the stick, thus reducing $\alpha$ in order to control the acceleration close to the region of 0.

Conclusion¶

This notebook presents the AFM OEI Take Off procedure. The user has to input the initial a/c and atmosperic conditions and then the code is able to produce an analytical decription of the take off procedure. The results are satisfying and produce an accurate description of the take off procedure. Then the user can post process the information as he likes.

Limitations¶

In this simulation there are several factors that were not taken into account:

  • CG Effect
  • Ground Effects
  • Contribution of the landing gear and doors in the aerodynamic coefficients
  • The thrust model is dependend only on speed, thus the altitude and temperature effects are not taken into account
  • The effect of speed on the $C_{zmax}$
  • The wind effect on the speed

These factors limit the accuracy of the model

References¶

  • Advanced Aircraf Flight Performance, Antionio Filippone, Cambridge University Press, 2012
In [ ]: